﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Liquifier;

namespace Liquifier
{
    [Liquifiable(Serialisation.Explicit, Serialisation.None, TypeFlags.DelegatesAndEvents)]
    public class ObservableList<T> : List<T>, INotifyCollectionChanged, INotifyPropertyChanged, ILiquify, ISolidify
    {
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public event PropertyChangedEventHandler PropertyChanged;

        [Liquify]
        public bool Singular = false;

        public ObservableList(bool singular)
        {
            this.Singular = singular;
            CollectionChanged += new NotifyCollectionChangedEventHandler(ObservableList_CollectionChanged);
        }

        public ObservableList()
        {
            CollectionChanged += new NotifyCollectionChangedEventHandler(ObservableList_CollectionChanged);
        }

        void ObservableList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (T item in e.NewItems)
                {
                    if (item is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged += ObservableList_PropertyChanged;
                    }
                }
            }
            if (e.OldItems != null)
            {
                foreach (T item in e.OldItems)
                {
                    if (item is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged -= ObservableList_PropertyChanged;
                    }
                }
            }
        }

        void ObservableList_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(sender, e);
        }

        public new T this[int index]
        {
            get
            {
                return base[index];
            }
            set
            {
                T olditem = base[index];
                base[index] = value;
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, olditem));
            }
        }

        public new void Add(T item)
        {
            if (base.Contains(item) && Singular)
                return;
            base.Add(item);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }

        public new void AddRange(IEnumerable<T> items)
        {
            foreach (T item in items)
            {
                Add(item);
            }
            //WPF does not like Range actions...
            //List<T> listofitems = new List<T>(items);
            //base.AddRange(listofitems);
            //CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, listofitems));
        }

        public new void Clear()
        {
            IList<T> items = this.ToList<T>();
            foreach (T item in items)
            {
                this.Remove(item);
            }
            //System.Collections.IList items = this.ToList<T>();
            //base.Clear();
            //CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,items));
        }

        public new void Insert(int index, T item)
        {
            base.Insert(index, item);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }

        public new void InsertRange(int index, IEnumerable<T> items)
        {
            base.InsertRange(index, items);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items));
        }

        public new void Remove(T item)
        {
            base.Remove(item);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
        }

        public new void RemoveAll(Predicate<T> match)
        {
            List<T> items = base.FindAll(match);
            base.RemoveAll(match);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
        }

        public new void RemoveAt(int index)
        {
            T item = base[index];
            base.RemoveAt(index);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
        }

        public new void RemoveRange(int index, int count)
        {
            List<T> items = base.GetRange(index, count);
            base.RemoveRange(index, count);
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
        }

        public void OnLiquify(Liquifer liquifier)
        {
            var items = this.ToArray();
            liquifier.AddEntryForSerialisation(items, "items", SerialisedMemberFlags.DoNotProcessOnSolidify);
        }

        public void OnSolidified(Liquifer solidifier)
        {
            var items = solidifier.GetDeserialisedEntry("items");
            this.AddRange(items as IEnumerable<T>);
        }
    }
}
